package com.goldentrail.parser;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import com.goldentrail.dataobject.DataArtifact;
import com.goldentrail.dataobject.Invocation;
import com.goldentrail.dataobject.Run;
import com.goldentrail.dataobject.User;
import com.goldentrail.dataobject.Workflow;
import com.goldentrail.db.DBInsert;

/**
 * This class would parse an xml file based on the specified DTD. 
 * It would load the xml file into DOM and allows xpath expressions 
 * to be used to get data from the DOM.
 * 
 * @author Dey, Saumen
 * @version 1.0, July 16, 2010
 * @version 1.1, July 07, 2011
 *
 */
public class ComadTraceParser {

	Document     dom;

	private Hashtable <String, DataArtifact>  data    = null;
	private Hashtable<String, Invocation>  invocationHt = null;
	/**
	 * Construct a ComadTraceParser object.
	 * 
	 * @param fineName the fully qualified file name
	 */	
	public ComadTraceParser(File fineName) throws Exception {
		parseXmlFile(fineName);
	}	

	public ComadTraceParser(){
	}
	
	/**
	 * Parsed the xml file and create a DOM.
	 * 
	 * @param fineName the fully qualified file name
	 */		
	private void parseXmlFile(File fineName) throws Exception {
	    try {      
		    DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
		    domFactory.setNamespaceAware(true); 
		    DocumentBuilder builder = domFactory.newDocumentBuilder();
		    dom  = builder.parse(fineName);
		    getData();
	    } catch(ParserConfigurationException e) {	
            e.printStackTrace();
	    } catch(SAXException e) {	    
            e.printStackTrace();
	    } catch(IOException e) {    
            e.printStackTrace();
	    }
	}	

	/**
	 * Accepts an XPath expression and retrieves the String data
	 * which matches with the XPath expression from the DOM.
	 * 
	 * @param expr the XPath expression
	 */			
	public String getRun() throws Exception {
		String        runId = null;
		Node          node  = null;
		NamedNodeMap  attr  = null;
        try {
        	NodeList nodes = parse("//Trace");
        	for (int i = 0; i < nodes.getLength(); i++) { 
        		node = nodes.item(i);
            	attr = node.getAttributes();
                for (int j = 0; j < attr.getLength(); j++) {
                	if (attr.item(j).getNodeName().equals("runId")) {
                		runId = attr.item(j).getNodeValue();
                	}
                }                
        	}
        } catch (Exception e) {
            e.printStackTrace();
        }
        return runId;
	}

	public void getData() {
		
		Node node = null;
		NamedNodeMap  attr  = null;
		DataArtifact dataArtifact = null;
		
		data = new Hashtable <String, DataArtifact>();
		
        try {
        	NodeList nodes = parse("//Data");
        	for (int i = 0; i < nodes.getLength(); i++) { 
        		node = nodes.item(i);
            	attr = node.getAttributes();
            	dataArtifact = new DataArtifact();
                for (int j = 0; j < attr.getLength(); j++) {
                	if (attr.item(j).getNodeName().equals("id")) {
                		dataArtifact.setDataArtifactId(attr.item(j).getNodeValue());
                	} else if (attr.item(j).getNodeName().equals("label")) {
                		dataArtifact.setValue(attr.item(j).getNodeValue());
                	} 
                }
                data.put(dataArtifact.getDataArtifactId(), dataArtifact);
        	}
        } catch (Exception e) {
            e.printStackTrace();
        }
        //System.out.println(data.toString());
	}

	/**
	 * Accepts an XPath expression and retrieves the String data
	 * which matches with the XPath expression from the DOM.
	 * 
	 * @param expr the XPath expression
	 */			
	public ArrayList<Invocation> getInvocations() throws Exception {
		
		String        invocationId = null;
		String        dataId = null;
		String        depDataId = null;
		
		Node          node  = null;
		NamedNodeMap  attr  = null;
		
		ArrayList<Invocation>  invocationList = new ArrayList<Invocation> ();
		invocationHt = new Hashtable<String, Invocation>();
		Invocation invocation = null;
		
		String cc1 = "CollectionComposer";
		String cc2 = "TraceReader";
		
        try {
        	NodeList nodes = parse("//Insertion");
        	for (int i = 0; i < nodes.getLength(); i++) { 
        		node = nodes.item(i);
            	attr = node.getAttributes();
            	
                for (int j = 0; j < attr.getLength(); j++) {               	
                	if (attr.item(j).getNodeName().equals("invocation")) {
                		invocationId = attr.item(j).getNodeValue();
                		System.out.println("invocationId: " + invocationId);
                		if ( !((invocationId.contains(cc1)) || (invocationId.contains(cc2))) ) {
                    		if (invocationHt.containsKey(invocationId)) {
                    			invocation = invocationHt.get(invocationId);
                    			System.out.println("if");
                    		} else {
                    			invocation = new Invocation();
                    			invocation.setInvocationId(invocationId);
                    			System.out.println("else");
                    		}	
                		}
                	} 
                }
                if ( !((invocationId.contains(cc1)) || (invocationId.contains(cc2))) ) {
                    for (int j = 0; j < attr.getLength(); j++) {
                    	if  (attr.item(j).getNodeName().equals("item")) {
                    		dataId = attr.item(j).getNodeValue();   // output data
                    		invocation.setOutputData(getOutputData(dataId, invocationId));
                    	} else if (attr.item(j).getNodeName().equals("dep")) {
                    		depDataId = attr.item(j).getNodeValue();  //input data
                    		invocation.setInputData(getInputData(depDataId));
                    	} 
                    }   
                    invocationHt.put(invocationId, invocation);   
                }
        	}
        	
        	Enumeration<Invocation> e = invocationHt.elements();
        	while (e.hasMoreElements()) {
        		invocationList.add(e.nextElement());
        	}
        	
        } catch (Exception e) {
            e.printStackTrace();
        }
        return invocationList;
	}

	/**
	 * Accepts an XPath expression and retrieves the String data
	 * which matches with the XPath expression from the DOM.
	 * 
	 * @param expr the XPath expression
	 */			
	public NodeList parse(String expr) throws Exception {
		NodeList nodes = null;
	     try {
	        XPathFactory factory = XPathFactory.newInstance();
	        XPath xpath = factory.newXPath();
	        XPathExpression expr1 = xpath.compile(expr);
	        Object result = expr1.evaluate(dom, XPathConstants.NODESET);
	        nodes = (NodeList) result;  
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
	    return nodes;
	}

	public Hashtable <String, DataArtifact> getInputData(String depDataIdList) {
		String[] depDataId = depDataIdList.split(" ");
		Hashtable <String, DataArtifact> inputData = null;
        try {
        	inputData = new Hashtable <String, DataArtifact>();
        	for (int i = 0; i < depDataId.length; i++) { 
        		if (data.containsKey(depDataId[i])) {
        			inputData.put(depDataId[i], data.get(depDataId[i]));
        		}                
        	}
        } catch (Exception e) {
            e.printStackTrace();
        }
		return inputData;
	}

	public Hashtable <String, DataArtifact> getOutputData(String dataId, String invocId) throws Exception {
		Hashtable <String, DataArtifact> outputData = null;
		outputData = new Hashtable <String, DataArtifact>();
		if (data.containsKey(dataId)) {
			outputData.put(dataId, data.get(dataId));
		} else {
			NodeList nodes = parse("//Collection[@id='" + dataId +"']");
            for (int i = 0; i < nodes.getLength(); i++) {   
            	parseColl(nodes.item(i), outputData, invocId);	               
            }
		}
		return outputData;
	}

	/**
	 * Accepts an XPath expression and retrieves the String data
	 * which matches with the XPath expression from the DOM.
	 * 
	 * @param expr the XPath expression
	 */			
	public void parseColl(Node node, Hashtable <String, DataArtifact> outputData, String invocId) throws Exception {
		 NodeList ndlist = null;
         try { 
             if (node.hasChildNodes()) {
            	 ndlist = node.getChildNodes();
             }            
             for (int i = 0; i < ndlist.getLength(); i++) {   
            	if (ndlist.item(i).getNodeName().equals("Collection")) {
            		parseColl(ndlist.item(i), outputData, invocId);            		
            	} else if (ndlist.item(i).getNodeName().equals("Data")) {
            		if (isNotDifferentProducer(ndlist.item(i), invocId)) {
                		addData(ndlist.item(i), outputData);
            		}   		
            	}                
             }
        } catch (Exception e) {
            e.printStackTrace();
        }
	}		

	public boolean isNotDifferentProducer(Node node, String invocId) {
		NamedNodeMap  attr  = null;		
		boolean flag = false;
        try {
        	attr = node.getAttributes();
            for (int j = 0; j < attr.getLength(); j++) {
            	if (attr.item(j).getNodeName().equals("id")) {  
        			System.out.println("dataId: " + attr.item(j).getNodeValue());
        			System.out.println("invocId: " + invocId);
        			System.out.println("producerId: " + getProducer(attr.item(j).getNodeValue()));
            		if (getProducer(attr.item(j).getNodeValue()) != null ) {
                		if (invocId.equals(getProducer(attr.item(j).getNodeValue()))) {
                			flag = true;
                		}	
            		} else {
            			flag = true;
            		}
            	} 
            }   
        } catch (Exception e) {
            e.printStackTrace();
        }    
        return flag;
	}
	
	public String getProducer(String dataId) {		
		Node          node  = null;
		NamedNodeMap  attr  = null;
		String        producerId = null;
        try {
        	NodeList nodes = parse("//Insertion[@item='" + dataId +"']");
            for (int i = 0; i < nodes.getLength(); i++) { 
        		node = nodes.item(i);
            	attr = node.getAttributes();
                for (int j = 0; j < attr.getLength(); j++) {               	
                	if (attr.item(j).getNodeName().equals("invocation")) {
                		producerId = attr.item(j).getNodeValue();
                	}
                }
            } 
        } catch (Exception e) {
            e.printStackTrace();
        }
		return producerId;
	}
	
	public void addData(Node node, Hashtable <String, DataArtifact> outputData) {
		NamedNodeMap  attr  = null;		
        try {
        	attr = node.getAttributes();
            for (int j = 0; j < attr.getLength(); j++) {
            	if (attr.item(j).getNodeName().equals("id")) {
            		if (data.containsKey(attr.item(j).getNodeValue())) {
            			outputData.put(attr.item(j).getNodeValue(), data.get(attr.item(j).getNodeValue()));
            		}                
            	} 
            }   
        } catch (Exception e) {
            e.printStackTrace();
        }        
	}
	
	public void insRunDetails(Run run, User user, Workflow wf) throws Exception {
		
		DBInsert dbi = new DBInsert();
		
		String runId = run.getRunId();
		
		if (!(dbi.isRunInDb(runId))) {
			//insert user
			dbi.insUser(user.getUserId(), user.getUserName());
			//insert workflow
			dbi.insWf(wf.getWfId(), wf.getWfName(), wf.getWfDesc(), wf.getUserId() );
			//insert run
			dbi.insRun(runId, runId, wf.getWfId());
			
			//insert actor, invocation, data, used, and genby
	    	String actorId = "";
	    	String invocId = null;
	    	Invocation invoc = null;
	    	DataArtifact data = null;
	    	
	    	ArrayList<Invocation> invocations  = run.getInvocations();
	    	for (Iterator<Invocation> it = invocations.iterator(); it.hasNext(); ) {
	    		invoc = it.next ();
	    		invocId = invoc.getInvocationId();
	    		
	    		actorId = invocId.substring(0,invocId.length()-2);
	    		dbi.insActor(actorId, actorId, "someUrl");
	    		
	    		dbi.insInvocation(invocId, actorId, runId);
	    		
		        Hashtable<String, DataArtifact> inData = invoc.getInputData();
		        Hashtable<String, DataArtifact> outData = invoc.getOutputData();
	            //insert Used deps
		    	Enumeration<DataArtifact> in = inData.elements();
		    	while (in.hasMoreElements()) {
		    		data = in.nextElement();
		    		dbi.insData(data.getDataArtifactId(), 
		    				    data.getValue(), 
		    				    data.getNodeUri());
		    		dbi.insUsed(invoc.getInvocationId(), data.getDataArtifactId());
		    	}
		    	//insert GenBy deps
		    	Enumeration<DataArtifact> out = outData.elements();
		    	while (out.hasMoreElements()) {
		    		data = out.nextElement();
		    		dbi.insData(data.getDataArtifactId(), 
		    				    data.getValue(), 
		    				    data.getNodeUri());
	    		    dbi.insGenBy(data.getDataArtifactId(), invoc.getInvocationId());
		    	}	    		
	    	}
			
		}

	
	}

}
